# coding=utf-8
"""

"""
import os
import re
import json
import tempfile
import subprocess
from urllib.parse import parse_qs
from flask import request, Blueprint, render_template, jsonify, url_for

from app.conf.err_msg import ErrClass
from app.curl.service.verify_code import verify_code
from utils.api_class_conf import token_fixture
from app.curl.service.convert_to import convert2gcp, write_excel
# from utils.beautify_json import syntax_highlight  # Json 美化交给 Js 处理, 接口返回会乱码 ...
from utils.curl_convert import curl_to_python, curl_to_table_data, curl_to_req_data, curl_to_simple_python
from utils.do_local_ip import local_ip
from utils.do_log import c_log
from utils.excel_path_conf import excel_path
from utils.do_redis import redis_cli

# @Time    :  2024-08-07 14:52:55
# @Author  :  jiangtong
# @Email   :  gingerqgyy@outlook.com
# @Project :  demo_flask_new
# @File    :  view_curl_to_py

# 蓝图 :
curl = Blueprint('curl', __name__, template_folder='templates')


# 首页 : curl 输入页 :
@curl.route('/', methods=['GET'])
def index():
    """
    首页, 首页用于输入 curl 命令
    """
    return render_template('curl_input.html')


@curl.route('/err_page', methods=['GET'])
def err_page():
    err_msg_info = request.args.get('err_msg', '系统未知异常!')
    return render_template('err_msg.html', err_msg=f'{err_msg_info}')


# 首页, curl 转 Python :
@curl.route('/curl_to_py', methods=['POST'])
def curl_to_py():
    curl_command = request.form['curl_command']
    # 简单判断下是不是 Curl 命令 :
    if 'curl' not in curl_command or 'http' not in curl_command:
        return render_template('err_msg.html', err_msg=f'{ErrClass.not_curl}')

    # curl 转 Python :
    try:
        python_code = curl_to_python(curl_command)
    except Exception as e:
        return render_template('err_msg.html', err_msg=f'{ErrClass.trans_error}, 失败原因: {e}')
    result = {}

    if 'execute' in request.form:
        # 临时文件存储 Python 代码
        with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as temp_file:
            temp_file.write(python_code.encode())
            temp_file_path = temp_file.name

        # 执行 Python 脚本并获取输出
        try:
            result = subprocess.check_output(['python', temp_file_path], stderr=subprocess.STDOUT, text=True, encoding='utf-8')
        except subprocess.CalledProcessError as e:
            err_info = e.output
            exception_info = str(err_info)
            pattern = r'raise (\w+Error)'
            matches = re.findall(pattern, exception_info)
            if matches:
                err_info = matches[0]
            return render_template('err_msg.html', err_msg=f'{ErrClass.py_err}, 失败原因: {err_info}')
        finally:
            # 清理临时文件
            import os
            os.remove(temp_file_path)

    return render_template('curl_convert.html', python_code=python_code, result=result, curl_command=curl_command)


# 首页, curl 转 Python (极简) :
@curl.route('/curl_to_simple_py', methods=['POST'])
def curl_to_simple_py():
    # 提取 curl :
    curl_command = request.form['curl_command']

    # 简单判断下是不是 Curl 命令 :
    if 'curl' not in curl_command or 'http' not in curl_command:
        return render_template('err_msg.html', err_msg=ErrClass.not_curl)

    try:
        python_code = curl_to_simple_python(curl_command)
    except Exception as e:
        return render_template('err_msg.html', err_msg=f'{ErrClass.trans_error}, 失败原因: {e}')
    result = {}

    if 'execute' in request.form:
        # 临时文件存储 Python 代码
        with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as temp_file:
            temp_file.write(python_code.encode())
            temp_file_path = temp_file.name

        # 执行 Python 脚本并获取输出
        try:
            result = subprocess.check_output(['python', temp_file_path], stderr=subprocess.STDOUT, text=True, encoding='utf-8')
        except subprocess.CalledProcessError as e:
            result = e.output
        finally:
            # 清理临时文件
            import os
            os.remove(temp_file_path)

    return render_template('curl_convert.html', python_code=python_code, result=result, curl_command=curl_command)


# py 执行 :
@curl.route('/execute_py', methods=['POST'])
def execute_py():
    # 数据提取 : ( python 代码和 curl 命令 )
    python_code = request.form['python_code']
    curl_command = request.form['curl_command']
    if not python_code or len(python_code) < 4:
        return render_template('err_msg.html', err_msg=f'{ErrClass.no_text}')

    # 临时文件存储 Python 代码
    with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as temp_file:
        temp_file.write(python_code.encode())
        temp_file_path = temp_file.name

    # 执行 Python 脚本并获取输出
    try:
        # result = subprocess.check_output(['python', temp_file_path], stderr=subprocess.STDOUT, text=True, encoding='utf-8')
        result = subprocess.check_output(['python3', temp_file_path], stderr=subprocess.STDOUT, text=True, encoding='utf-8')
    except subprocess.CalledProcessError as e:
        err_info = e.output
        exception_info = str(err_info)
        pattern = r'raise (\w+Error)'
        matches = re.findall(pattern, exception_info)
        if matches:
            err_info = matches[0]
        return render_template('err_msg.html', err_msg=f'{ErrClass.py_err}, 失败原因: {err_info}')
    finally:
        # 清理临时文件
        import os
        os.remove(temp_file_path)

    return render_template('curl_convert.html', python_code=python_code, result=result, curl_command=curl_command)


# 页面提交 curl 命令, 方法中处理好, 返回重定向页面 :
@curl.route('/curl_to_table', methods=['POST'])
def curl_to_table():
    # 提取 curl :
    _rea_json_data = request.get_json()
    _curl_command = _rea_json_data.get('curl')

    # 简单判断下是不是 Curl 命令 :
    if 'curl' not in _curl_command or 'http' not in _curl_command:
        redirect_url = url_for('curl.err_page', err_msg=ErrClass.not_curl)
        return jsonify({'redirect': redirect_url})

    # return render_template('err_msg.html', err_msg=f'😡🤡👋 好好输入 Curl 命令, 别在这里找 Bug ! 👋🤡😡')

    # 提取
    case_id = _rea_json_data.get('case_id')
    api_name = _rea_json_data.get('api_name')
    # sheet_name = _rea_json_data.get('sheet_name', 'Sheet页')
    case_model = _rea_json_data.get('case_model', '模块名')
    case_title = _rea_json_data.get('case_title', '用例名')

    # 解析 curl 命令, curl 转 table_data :
    try:
        _req_data = curl_to_table_data(_curl_command)
    except Exception as e:
        redirect_url = url_for('curl.err_page', err_msg=f'{ErrClass.trans_error}, 失败原因: {e}')
        return jsonify({'redirect': redirect_url})

    # 处理 params : ( 过滤掉 _t 参数，并将其他参数转换为字典 )
    parsed_params = parse_qs(_req_data.params)
    data = {k: v[0] for k, v in parsed_params.items() if k != '_t'}

    # 取 table_data 的数据 :
    host = _req_data.host
    url = _req_data.uri
    method = _req_data.method

    # 处理 data :
    data = data if data else _req_data.data
    try:
        data = json.dumps(data)
    except json.JSONDecodeError:
        data = str(_req_data.data)
    precondition = '无'
    expected = 'OK'

    _url_key = 'lock' if 'lock' in url else method

    # 处理 case_id, name, model
    """ # 已弃用, 改为使用页面填写的数据 .
    case_id: str = action_code.get(_url_key, '')
    case_name = action_name.get(_url_key, '') if not case_title else case_title
    model = '模块'
    url_codes = url.split('/')
    for code in url_codes:
        if code:
            case_id = action_code.get(method) + code
            try:
                model = translator.translate(text=code, dest='zh-cn').text if is_trans else case_id
            except (ConnectionError,):
                # 翻译失败, 则直接用 case_id :
                model = case_id
            case_name += model
            break
    # """

    redirect_url = url_for('curl.curl_to_excel_data',
                           case_id=case_id,
                           api_name=api_name,
                           model=case_model,
                           title=case_title,
                           host=host,
                           url=url,
                           method=method,
                           data=data,
                           precondition=precondition,
                           expected=expected
                           )

    return jsonify({'redirect': redirect_url})


# curl 提取 excel 数据的展示页 :
@curl.route('/curl_to_excel_data')
def curl_to_excel_data():
    """
    页面 Excel 表格数据
    可自动成成的数据仅有:
        host
        url
        method
        data
    还有一些可写死的, 比如请求头是 '无', 预期结果是 'OK' 等..

    如果再进一步, 也可以根据 url 的后缀和请求方法, 自动生成 case_id, 肯定不准, 但是也可参考
    比如 /form_data/snapshot/submit 的 post 请求 ----> add_snapshot, 新增快照, 就不准
    但是 /health_education/lock 的 put 请求 update_health_education, 更新宣教, 就还行

    :return : curl_to_excel_data.html
    """
    host = request.args.get('host', 'h')
    url = request.args.get('url', '')
    method = request.args.get('method', 'get')
    data = request.args.get('data', {})
    api_name = request.args.get('api_name', {})
    title = request.args.get('title', {})

    try:
        data = json.loads(data)
        data = json.dumps(data)
    except json.JSONDecodeError:
        # 遇到异常, 原样展示即可,
        pass

    case_id = request.args.get('case_id', 'h')
    case_name = request.args.get('case_name', 'h')
    model = request.args.get('model', 'h')

    return render_template(template_name_or_list='curl_to_excel_data.html',
                           case_id=case_id,
                           api_name=api_name,
                           case_name=case_name,
                           title=title,
                           model=model,
                           host=host,
                           url=url,
                           method=method,
                           data=data,
                           )


# 美化 Json : ( 美化 curl 中的 data , 独立显示 )
@curl.route('/beautify_json', methods=['GET', 'POST'])
def json_beautify():
    """
    Json 美化页
    """

    # data 默认是空字典 :
    data = {}
    if request.method == 'POST':
        curl_command = request.form.get('curl_command', '')
        # 解析并提取出 JSON 部分
        try:
            _req_data = curl_to_req_data(curl_command)
            json_data = _req_data.data
            # json_data = json.dumps(data)
            # json_object = json.loads(json_data)
            formatted_json = json.dumps(json_data, indent=4)

            # 处理 url 拼接的参数, 如果即存在 url 拼接的参数, 又存在 data 参数, 则取 data 参数 :
            if _req_data.params:
                parsed_params = parse_qs(_req_data.params)
                if not data:
                    formatted_json = json.dumps({k: v[0] for k, v in parsed_params.items() if k != '_t'}, indent=4, ensure_ascii=False)

            # 对 JSON 进行语法高亮处理 ( 已弃用, Python 中美化后, 模板渲染会乱码, 直接交给 Js 美化, 不乱码 )
            # formatted_json_html = syntax_highlight(formatted_json)
        except (ValueError, TypeError):
            # formatted_json_html = '<span class="json-error">Invalid JSON data</span>'
            formatted_json = {}
            pass
    else:
        # formatted_json_html = {}
        formatted_json = {}
        pass

    return render_template('beautify_json.html', json_data=formatted_json)


# Json 美化 : ( 美化 curl 中的 data , data 和 json 同时显示 )
@curl.route('/beautify_json_page', methods=['GET', 'POST'])
def json_beautify_page():
    """
    Json 美化页
    如果 curl 输入框中输入了 curl 命令, 则直接美化 curl 中的数据
    如果 curl 输入框中未输入 curl 命令, 则直接进入美化页
    """

    # data 默认是空字典 :
    json_data = ""

    # 获取 curl 命令 :
    if request.method == 'POST':
        _curl_command = request.form.get('curl_command', '')
        # 解析 curl 命令, curl 转 table_data
        if _curl_command:
            try:
                _req_data = curl_to_req_data(_curl_command)
                data = _req_data.data
                json_data = json.dumps(data, indent=4, sort_keys=True)

                # 处理 url 拼接的参数, 如果即存在 url 拼接的参数, 又存在 data 参数, 则取 data 参数 :
                if _req_data.params:
                    parsed_params = parse_qs(_req_data.params)
                    if not data:
                        json_data = json.dumps({k: v[0] for k, v in parsed_params.items() if k != '_t'}, indent=4, ensure_ascii=False)

            except Exception as e:
                return render_template('err_msg.html', err_msg=f'美化失败, 失败原因: {e}')

    return render_template('beautify_json_page.html', json_data=json_data)


# Python 脚本, Pytest 脚本, Excel 数据
@curl.route('/curl_to_pytest_excel', methods=['POST'])
def convert_to_all():
    """
    接收 curl, case_id, case_name, case_model 等参数,
    生成 Python 脚本, Pytest 脚本, Excel 数据
    TODO: 方法优化, 当前和转表格代码重复, 维护量加倍, 需要提取, 最好是以类和对象来组织数据, 再将数据处理部分提取到公共方法中
    :return : curl_to_excel_data.html
    """
    # 提取提交的数据
    curl_command = request.form.get('curl_command', '')
    if not curl_command:
        return render_template('err_msg.html', err_msg=ErrClass.no_text)

    # 简单判断下是不是 Curl 命令 :
    if 'curl' not in curl_command or 'http' not in curl_command:
        redirect_url = url_for('curl.err_page', err_msg=ErrClass.not_curl)
        return jsonify({'redirect': redirect_url})

    # 解析 curl 命令, curl 转 table_data
    _req_data = curl_to_table_data(curl_command)

    # 处理 params : ( 过滤掉 _t 参数，并将其他参数转换为字典 )
    parsed_params = parse_qs(_req_data.params)
    data = {k: v[0] for k, v in parsed_params.items() if k != '_t'}

    # 取 table_data 的数据 :
    host = _req_data.host
    url = _req_data.uri
    method = _req_data.method
    headers = _req_data.headers
    _terminal = headers.get('terminal', 'h')

    # epic by token :
    _token = _req_data.api_token
    epic = '全病程管理'
    if _token == token_fixture.get('SUPER_USER'):
        epic = '运营端'
    elif _token == token_fixture.get('HEALTH_MANAGEMENT'):
        epic = '健管师App'
        if _terminal == 'h':
            epic = '健管师PC'
    elif _token == token_fixture.get('DOCTOR'):
        epic = '执业版App'
    elif _token == token_fixture.get('PUBLIC_USER'):
        epic = '医链健康App'

    # api_class :
    _api_class = _req_data.api_class

    # 处理 data :
    data = data if data else _req_data.data
    try:
        data = json.dumps(data)
    except json.JSONDecodeError:
        data = str(_req_data.data)

    # 处理 url 最后的 id :
    uri_id = ','
    if _req_data.uri_id:
        uri_id = ", i_d = do_conf.get('xxx')"
        if not _req_data.uri_id.isdigit():
            uri_id_pre_fix = '{do_conf.get("xxx")}'
            uri_id_postfix = '/'.join(_req_data.uri_id.split('/')[1:])
            uri_id = f", i_d = f'{uri_id_pre_fix}/{uri_id_postfix}'"

    _url_key = 'lock' if 'lock' in url else method

    # 处理 sheet_name, case_id, name, model, title :
    sheet_name = request.form.get('sheet_name')
    sheet_name = sheet_name if sheet_name else 'Sheet页'

    case_id = request.form.get('case_id', 'case_id')
    case_id = case_id if case_id else '编号'

    api_name = request.form.get('api_name', 'case_name')
    api_name = api_name if api_name else '接口名称'

    model = request.form.get('case_model', '模块')
    model = model if model else '模块'

    title = request.form.get('case_title')
    title = title if title else f'{api_name}成功'

    # 用例方法 :
    pytest_test_code = f"""
    # xxx : 
    @pytest.mark.parametrize('case_data', all_data.get('{sheet_name}', '{case_id}'))
    def test_{case_id}(self, case_data, {_token}):
        \"\"\" 用例说明 \"\"\"
        
        # 准备数据 :
        case_data.data['id'] = do_conf.get('xxx')
        
        # 调用接口 : 
        _res = {_api_class}.call_assert(case_data{uri_id})
        
        # 数据处理 :
        base_extract.json2set(target=_res, key='', new_key='')
"""

    # 用例类 :
    pytest_class_code = f"""
import allure
import pytest

from common import all_data, do_conf, base_extract, is_del, BaseApi, BaseAssert


@allure.epic('{epic}')
@allure.feature('00X模块_{model}模块')
class TestcaseXxxx:
    \"\"\" 用例类说明 \"\"\"

    {pytest_test_code}
"""

    # 返回页面 :
    return render_template(template_name_or_list='curl_to_pytest_excel.html',
                           pytest_class_code=pytest_class_code,
                           pytest_test_code=pytest_test_code,
                           case_id=case_id,
                           api_name=api_name,
                           model=model,
                           title=title,
                           host=host,
                           url=url,
                           method=method,
                           data=data,
                           )


# Python 脚本, GCP ,
@curl.route('/curl_to_gcp', methods=['POST'])
def convert_to_gcp():
    # 提取提交的数据
    curl_command = request.form.get('curl_command', '')
    if not curl_command:
        return render_template('err_msg.html', err_msg=ErrClass.no_text)

    # 简单判断下是不是 Curl 命令 :
    if 'curl' not in curl_command or 'http' not in curl_command:
        redirect_url = url_for('curl.err_page', err_msg=ErrClass.not_curl)
        return jsonify({'redirect': redirect_url})

    # 处理 sheet_name, case_id, name, model, title :
    sheet_name = request.form.get('sheet_name')
    case_id = request.form.get('case_id', 'case_id')
    api_name = request.form.get('api_name', 'case_name')
    model = request.form.get('case_model', '模块')
    title = request.form.get('case_title', )
    title = title if title else f'{api_name}成功'

    converted_data = convert2gcp(curl_command, sheet_name=sheet_name, case_id=case_id, api_name=api_name, model=model, title=title)

    # 返回页面 :
    return render_template(template_name_or_list='curl_to_pytest_excel.html', **converted_data, )


# Python 脚本, EDC ,
@curl.route('/curl_to_edc', methods=['POST'])
def convert_to_edc():
    # 提取提交的数据
    curl_command = request.form.get('curl_command', '')
    if not curl_command:
        return render_template('err_msg.html', err_msg=ErrClass.no_text)

    # 简单判断下是不是 Curl 命令 :
    if 'curl' not in curl_command or 'http' not in curl_command:
        redirect_url = url_for('curl.err_page', err_msg=ErrClass.not_curl)
        return jsonify({'redirect': redirect_url})
    # 返回页面 :
    return render_template(template_name_or_list='curl_to_pytest_excel.html',
                           pytest_class_code=None,
                           pytest_test_code=None,
                           case_id=None,
                           api_name=None,
                           model=None,
                           title=None,
                           host=None,
                           url=None,
                           method=None,
                           data=None,
                           python_code='import requests'
                           )


# Excel 写入 :
@curl.route('/excel_write', methods=['POST'])
def excel_write():
    """
    写入 Excel
    只有本机调用本机服务时, 才会写入 Excel, 非本机调用情况下会给出提示
    Excel 文件选择时, 只需要保证选择的 Excel 文件正确即可;
    因为 Js 无法获取到文件的绝对路径, 只能获取到文件名, 故写入 Excel 时, Js 只决定文件名;
    也就是说, 你的 yl_test_api 项目目录多深都没关系, 你只需要将目标 Excel 复制一份到桌面即可, 每次选择就会很方便;
    实现上, Excel 的路径信息是写死的, 在配置文件中指定, 因此当 yl_test_api 项目在电脑中位置发生变化时, 请同步更新配置;
    首次拉取项目到本机时, 如果需要自动写入 Excel, 请更新配置 ( /utils/excel_path_conf.py )
    写入逻辑已经尽可能完善 :
        1. 仅支持本机调用本机情况下进行写入
        2. Excel 路径校验
        3. Excel 格式校验
        4. Excel 存在校验
        5. Excel 权限校验 ( 如果本机打开了 Excel, 则写入失败 )
        6. Sheet 名称校验
        7. case_id 重复校验, case_id 重复情况下, 不进行写入
    """
    # 获取访问者的 IP 地址
    visitor_ip = request.remote_addr
    is_localhost = True if visitor_ip == '127.0.0.1' or visitor_ip == 'localhost' or visitor_ip == local_ip() else False
    c_log.info(f'访问者 IP: {visitor_ip}, 是否为本机: {"是" if is_localhost else "否"}')
    if not is_localhost:
        return {'status': '0', 'msg': '拿你的电脑启服务去, 别搞我的 Excel 文件'}
    # 请求参数获取 :
    _rea_json_data = request.get_json()
    _excel_path = _rea_json_data.get('excel_path')
    _excel_path = os.sep.join([excel_path, _excel_path])
    _excel_data = _rea_json_data.get('excel_data')
    sheet_name = _rea_json_data.get('sheet_name')
    _res = write_excel(_excel_path, _excel_data, sheet_name)
    response = jsonify(_res)
    return response


# 验证码 :
@curl.route('/get_verify_code', methods=['POST'])
def get_verify_code():
    # 获取请求数据中的环境信息 :
    _req_json_data = request.get_json()
    if not _req_json_data or _req_json_data.get('venv') not in ['test', 'pre', 'beta']:
        return {'status': '0', 'msg': '数据有误!'}
    venv = _req_json_data.get('venv')

    # 获取验证码 :
    """
    codes = [
        {'phone': '1', 'verify_code': '1111', 'create_time': '1111'},
        {'phone': '2', 'verify_code': '2222', 'create_time': '2222'},
        {'phone': '3', 'verify_code': '3333', 'create_time': '3333'},
    ]
    # """

    codes = verify_code(venv=venv)

    if not codes:
        return {'status': '0', 'msg': '获取验证码数据失败, 请使用 Navicat 获取!'}

    return {'status': '2', 'msg': '获取验证码成功', 'datas': codes}


# 验证码 :
@curl.route('/get_redis_verify_code', methods=['POST'])
def get_redis_verify_code():
    _req_json_data = request.get_json()
    if not _req_json_data or not _req_json_data.get('verify_id'):
        return {'status': '1', 'msg': '数据有误!'}
    _verify_code = redis_cli.get(f'AUTH:VERIFY_CODE:{_req_json_data.get("verify_id")}')
    if _verify_code:
        return {'status': '0', 'msg': '查询成功', 'data': _verify_code}
    return {'status': '1', 'msg': '未查询到!'}


@curl.route('/get_pool_credited_people', methods=['POST'])
def get_pool_credited_people():
    return render_template('err_msg.html', err_msg='还没写呢, 别猴急~')
